package net.siufung.boot.jwt.service.impl;

import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.StrUtil;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.MalformedJwtException;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.SignatureException;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import net.siufung.boot.jwt.entity.JwtToken;
import net.siufung.boot.jwt.properties.JwtProperties;
import net.siufung.boot.jwt.service.IJwtService;
import net.siufung.boot.jwt.utils.JwtTokenUtil;
import net.siufung.core.utils.StringPool;

import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.temporal.ChronoUnit;
import java.util.Date;
import java.util.Map;
import java.util.Objects;

/**
 * @author 陈建峰
 * @since 2022/5/1 6:19 下午
 */
@Slf4j
@RequiredArgsConstructor
public class JwtServiceImpl implements IJwtService {

    @NonNull
    private final JwtProperties jwtProperties;

    @Override
    public String createJwt(String subject, Map<String, Object> claims, int expireMinutes) {
        LocalDateTime creationTime = Convert.toLocalDateTime(claims.get(JwtTokenUtil.CLAIM_KEY_CREATE_DATE));
        Date expireDate = Date.from((
                Objects.isNull(creationTime)? LocalDateTime.now(): creationTime)
                .plus(expireMinutes, ChronoUnit.MINUTES).atZone(ZoneId.systemDefault()).toInstant());
        return Jwts.builder()
                .setSubject(StrUtil.isNotBlank(subject)? subject: StringPool.EMPTY)
                .setClaims(claims)
                .setExpiration(expireDate)
                .signWith(SignatureAlgorithm.forName(jwtProperties.getAlgorithm()), jwtProperties.getSigningKey())
                .compact();
    }

    @Override
    public String createJwt(Map<String, Object> claims, int expireMinutes) {
        return createJwt(StrUtil.EMPTY, claims, expireMinutes);
    }

    @Override
    public JwtToken createJwtToken(Map<String, Object> claims, int expireMinutes) {
        return new JwtToken(createJwt(claims, expireMinutes), jwtProperties.getSigningKey(), expireMinutes);
    }

    @Override
    public Claims parseJwt(String token) {
        return  Jwts.parser()
                .setSigningKey(jwtProperties.getSigningKey().getBytes())
                .parseClaimsJws(token)
                .getBody();
    }

    @Override
    public boolean isInvalid(String token) {
        try {
            parseJwt(token);
            return Boolean.FALSE;
        } catch (SignatureException | ExpiredJwtException | MalformedJwtException ex) {
            ex.printStackTrace();
        }
        return Boolean.TRUE;
    }

    @Override
    public Date getExpireDate(String token) {
        try {
            return parseJwt(token).getExpiration();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    @Override
    public void setExpireDate(String token, Date expireDate) {
        try {
            parseJwt(token).setExpiration(expireDate);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    @Override
    public boolean isExpired(String token) {
        return getExpireDate(token).before(new Date());
    }

    @Override
    public String refreshJwt(String token, String subject, int expireMinutes) {
        try {
            Claims claims = parseJwt(token);
            claims.put(JwtTokenUtil.CLAIM_KEY_CREATE_DATE, new Date());
            return createJwt(subject, claims, expireMinutes);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    @Override
    public String refreshJwt(String token, int expireMinutes) {
        return refreshJwt(token, StrUtil.EMPTY, expireMinutes);
    }

    @Override
    public JwtToken refreshJwtToken(JwtToken jwtToken) {
        try {
            Claims claims = parseJwt(jwtToken.getToken());
            claims.put(JwtTokenUtil.CLAIM_KEY_CREATE_DATE, new Date());
            jwtToken.setToken(
                    createJwt(claims, jwtToken.getExpireMinutes()));
        } catch (Exception e) {
            e.printStackTrace();
        }
        return jwtToken;
    }

    @Override
    public JwtToken refreshJwtToken(String jwtToken, int expireMinutes) {
        try {
            Claims claims = parseJwt(jwtToken);
            claims.put(JwtTokenUtil.CLAIM_KEY_CREATE_DATE, new Date());
            return new JwtToken(createJwt(claims, expireMinutes), jwtProperties.getSigningKey(), expireMinutes);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}
